package com.self.match.service.impl;

import com.self.common.enums.OrderDirection;
import com.self.common.param.ExchangeTrade;
import com.self.common.param.Order;
import com.self.match.enums.MatchStrategy;
import com.self.match.model.MergeOrder;
import com.self.match.model.OrderBooks;
import com.self.match.model.TradePlate;
import com.self.match.rocketmq.OrderProducer;
import com.self.match.service.MatchService;
import com.self.match.service.MatchServiceFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.MimeTypeUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @version v1.0
 * @ClassName: MatchServiceImpl
 * @Description:
 * @Author: Mac
 */
@Service
@Slf4j
public class MatchServiceImpl implements MatchService, InitializingBean {

    @Autowired
    private OrderProducer orderProducer;

    @Override
    public void match(OrderBooks orderBooks, Order order) {
        log.info("开始撮合，订单账本:{},订单:{}",orderBooks,order);
        //取消订单
        if (order.getIfCancel() != null && order.getIfCancel()){
            orderBooks.cancelOrder(order);
            Message<Order> message = MessageBuilder.withPayload(order)
                    .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON_VALUE)
                    .build();
            orderProducer.cancelOrderOut().send(message);
            return;
        }
        //数据校验
        if (order.getPrice().compareTo(BigDecimal.ZERO) <= 0){
            return;
        }
        //获取一个挂单迭代器
        Iterator<Map.Entry<BigDecimal, MergeOrder>> currentOrderIterator = null;
        if (order.getOrderDirection() == OrderDirection.BUY){
            currentOrderIterator = orderBooks.getCurrentOrderIterator(OrderDirection.SELL);
        }else{
            currentOrderIterator = orderBooks.getCurrentOrderIterator(OrderDirection.BUY);
        }
        //交易记录
        List<ExchangeTrade>  exchangeTrades = new ArrayList<>();
        //已经完成订单
        List<Order> completedOrders = new ArrayList<>();
        // 是否退出循环
        boolean exitLoop = true;
        //循环挂单进行撮合
        while (currentOrderIterator.hasNext() && exitLoop){
            Map.Entry<BigDecimal, MergeOrder> next = currentOrderIterator.next();
            BigDecimal markerPrice  = next.getKey();
            MergeOrder markerMergeOrder  = next.getValue();
            //校验
            if (order.getOrderDirection() == OrderDirection.BUY && order.getPrice().compareTo(markerPrice) < 0){
                break;
            }
            if (order.getOrderDirection() == OrderDirection.SELL && order.getPrice().compareTo(markerPrice) > 0){
                break;
            }
            //遍历合单
            Iterator<Order> iterator = markerMergeOrder.iterator();
            while (iterator.hasNext()){
                Order marker  = iterator.next();
                //撮合生成交易记录
                ExchangeTrade exchangeTrade =  processMath(order, marker, orderBooks);
                exchangeTrades.add(exchangeTrade);
                //委托单需要的数量已经满足
                if (order.isCompleted()){
                    completedOrders.add(order);
                    //推出最外层循环
                    exitLoop = false;
                    break;
                }
                //挂单完成了
                if (marker.isCompleted()){
                    completedOrders.add(order);
                    iterator.remove();
                }
            }
            //如果订单没有完成
            if (order.getAmount().compareTo(order.getTradedAmount()) > 0){
                //进行下一轮撮合
                orderBooks.addOrder(order);
            }
            // 发送交易记录
            if (exchangeTrades.size() > 0){
                handlerExchangeTrades(exchangeTrades);
            }

            if (completedOrders.size() > 0){
                // 发送已经成交的交易记录
                handlerCompletedOrders(completedOrders);
                //发送判断数据，更新盘口
                TradePlate tradePlate = order.getOrderDirection() == OrderDirection.BUY ? 
                        orderBooks.getBuyTradePlate() : orderBooks.getSellTradePlate();
                //发送盘口
                sendTradePlateData(tradePlate);
            }

        }

    }

    /**
     * @Title: 发送完成的订单
     * @Description:
     * @Param [tradePlate]
     * @Return void
     * @Author Mac
     * @Throws
     */
    private void handlerCompletedOrders(List<Order> completedOrders) {
        Message<List<Order>> message = MessageBuilder.withPayload(completedOrders)
                .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON_VALUE)
                .build();
        orderProducer.completedOrdersOut().send(message);
    }

    /**
     * @Title: 发送盘口数
     * @Description:
     * @Param [tradePlate]
     * @Return void
     * @Author Mac
     * @Throws
     */
    private void sendTradePlateData(TradePlate tradePlate) {
        Message<TradePlate> message = MessageBuilder.withPayload(tradePlate)
                .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON_VALUE)
                .build();
        orderProducer.plateOut().send(message);
    }

    /**
     * @Title: 发送完成的订单
     * @Description:
     * @Param [tradePlate]
     * @Return void
     * @Author Mac
     * @Throws
     */
    private void handlerExchangeTrades(List<ExchangeTrade> exchangeTrades) {
        Message<List<ExchangeTrade>> message = MessageBuilder.withPayload(exchangeTrades)
                .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON_VALUE)
                .build();
        orderProducer.exchangeTradesOut().send(message);
    }

    /**
     * @Title: 进行委托单的匹配撮合交易
     * @Description:
     * @Param [taker, marker, orderBooks]
     * @Return com.self.match.model.ExchangeTrade
     * @Author Mac
     * @Throws
     */
    private ExchangeTrade processMath(Order taker, Order marker, OrderBooks orderBooks) {
         //成交价格
        BigDecimal dealPrice  = marker.getPrice();
        //成交量
        BigDecimal turnoverAmount = BigDecimal.ZERO;
        //本次成交数量
        BigDecimal needAmount = calcTradeAmount(taker);
        //本次供给数量
        BigDecimal providerAmount = calcTradeAmount(marker);

        turnoverAmount = needAmount.compareTo(providerAmount) <= 0?needAmount:providerAmount;
        if (turnoverAmount.compareTo(BigDecimal.ZERO) < 0){
            return null;
        }
        //设置本次成交数据
        taker.setTradedAmount(taker.getTradedAmount().add(turnoverAmount));
        taker.setTurnover(turnoverAmount.multiply(dealPrice).setScale(orderBooks.getCoinScale(), RoundingMode.HALF_UP));
        //设置挂单成交数据
        marker.setTradedAmount(marker.getTradedAmount().add(turnoverAmount));
        BigDecimal markerTurnover = turnoverAmount.multiply(dealPrice).setScale(orderBooks.getBaseCoinScale(), RoundingMode.HALF_UP);
        marker.setTurnover(markerTurnover);

        ExchangeTrade exchangeTrade = new ExchangeTrade();
        // 设置购买的数量
        exchangeTrade.setAmount(turnoverAmount);
        // 设置购买的价格
        exchangeTrade.setPrice(dealPrice);
        // 设置成交的时间
        exchangeTrade.setTime(System.currentTimeMillis());
        // 设置成交的交易对
        exchangeTrade.setSymbol(orderBooks.getSymbol());
        // 设置交易的方法
        exchangeTrade.setDirection(taker.getOrderDirection());
        // 设置出售方的id
        exchangeTrade.setSellOrderId(marker.getOrderId());
        // 设置买方的id
        exchangeTrade.setBuyOrderId(taker.getOrderId());
        // 设置买方的交易额
        exchangeTrade.setBuyTurnover(taker.getTurnover());
        // 设置卖方的交易额
        exchangeTrade.setSellTurnover(marker.getTurnover());
        //删除盘口
        if (taker.getOrderDirection() == OrderDirection.BUY){
            orderBooks.getBuyTradePlate().remove(marker,turnoverAmount);
        }else{
            orderBooks.getSellLimitPrice().remove(marker,turnoverAmount);
        }
        return exchangeTrade;
    }

    /**
     * @Title: 计算成交数量
     * @Description:
     * @Param [order]
     * @Return java.math.BigDecimal
     * @Author Mac
     * @Throws
     */
    private BigDecimal calcTradeAmount(Order order) {
        return order.getAmount().subtract(order.getTradedAmount());
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        MatchServiceFactory.addMatchService(MatchStrategy.LIMIT_PRICE,this);
    }
}
